package cn.virens.web.components.shiro.simple.simple;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import cn.hutool.core.util.StrUtil;
import cn.virens.common.RequestUtil;
import cn.virens.web.components.shiro.ShiroAuthInterface;
import cn.virens.web.components.shiro.exception.CaptchaErrorException;

/**
 * 登录表单校验
 * 
 * @文件   :SimpleAuthcFilter.java
 * @作者   :cn.loioi
 * @创建时间 :2016年10月13日 下午12:48:59
 */
public class SimpleAuthorizingFilter extends FormAuthenticationFilter {
	private Logger logger = LoggerFactory.getLogger(SimpleAuthorizingFilter.class);

	@Autowired
	@Qualifier(ShiroAuthInterface.NAME)
	private ShiroAuthInterface shiroAuthInterface;

	private boolean useCaptcha = false;// 是否使用验证码
	private String captchaParam = "captcha";// 验证码的参数名

	/**
	 * 登录前置处理
	 */
	@Override
	public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
		logger.debug("onPreHandle");

		/** 如果已经登录，并且访问的地址是登录页面，直接重定向到登录成功页面 */
		if (isAccessAllowed(request, response, mappedValue)) {
			if (isLoginRequest(request, response)) {
				WebUtils.issueRedirect(request, response, getSuccessUrl());
				return false;
			} else {
				return true;
			}
		} else {
			return onAccessDenied(request, response, mappedValue);
		}
	}

	/**
	 * 执行登录操作
	 */
	@Override
	protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
		logger.info("executeLogin,RememberMe:" + isRememberMe(request));

		if (isUseCaptcha() && verifyCactcha(request, response) == false) {
			return onLoginFailure(null, new CaptchaErrorException(), request, response);
		} else {
			return super.executeLogin(request, response);
		}
	}

	/**
	 * 登录成功
	 */
	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		this.shiroAuthInterface.onLoginSuccess(getUsername(request), getHost(request));

		return super.onLoginSuccess(token, subject, request, response);
	}

	/**
	 * 登录失败
	 */
	@Override
	protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
		this.shiroAuthInterface.onLoginFailure(getUsername(request), getHost(request));

		return super.onLoginFailure(token, e, request, response);
	}

	/**
	 * 登录失败
	 */
	@Override
	protected void setFailureAttribute(ServletRequest request, AuthenticationException e) {
		if (e instanceof CaptchaErrorException) {
			request.setAttribute("success", false);
			request.setAttribute("message", "验证码错误");
			request.setAttribute(getFailureKeyAttribute(), e);
		} else if (e instanceof UnknownAccountException) {
			request.setAttribute("success", false);
			request.setAttribute("message", "账号错误");
			request.setAttribute(getFailureKeyAttribute(), e);
		} else if (e instanceof IncorrectCredentialsException) {
			request.setAttribute("success", false);
			request.setAttribute("message", "密码错误");
			request.setAttribute(getFailureKeyAttribute(), e);
		} else {
			request.setAttribute("success", false);
			request.setAttribute("message", "未知错误");
			request.setAttribute(getFailureKeyAttribute(), e);
		}
	}

	/**
	 * 是否需要验证码
	 * 
	 * @return
	 */
	public boolean isUseCaptcha() {
		return useCaptcha;
	}

	/**
	 * 设置是否需要验证码
	 * 
	 * @param useCaptcha
	 */
	public void setUseCaptcha(boolean useCaptcha) {
		this.useCaptcha = useCaptcha;
	}

	/**
	 * 获取验证码参数字段名
	 * 
	 * @return
	 */
	public String getCaptchaParam() {
		return captchaParam;
	}

	/**
	 * 设置验证码参数字段名
	 * 
	 * @param captchaParam
	 */
	public void setCaptchaParam(String captchaParam) {
		this.captchaParam = captchaParam;
	}

	@Override
	protected String getHost(ServletRequest request) {
		return RequestUtil.getRemoteAddr((HttpServletRequest) request);
	}

	/**
	 * 获取验证码
	 * 
	 * @param  request
	 * @return
	 */
	protected String getCaptcha(ServletRequest request) {
		return request.getParameter(getCaptchaParam());
	}

	/**
	 * 获取缓存在Session里的验证码
	 * 
	 * @param  request
	 * @param  response
	 * @return
	 */
	protected String getCaptcha(ServletRequest request, ServletResponse response) {
		Subject subject = getSubject(request, response);
		if (subject == null) { return null; }
		Session session = subject.getSession(false);
		if (session == null) { return null; }
		return String.valueOf(session.getAttribute(getCaptchaParam()));
	}

	private boolean verifyCactcha(ServletRequest request, ServletResponse response) {
		String tcode1 = getCaptcha(request);
		String tcode2 = getCaptcha(request, response);

		logger.debug("验证码：{}/{}", tcode1, tcode2);

		return StrUtil.equalsIgnoreCase(tcode1, tcode2);
	}
}
